﻿using NMF.Expressions;
using NMF.Expressions.Linq;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using System.Text.RegularExpressions;


namespace Test
{
    class Program
    {
        private static Dictionary<string, Person> peopleDict = new Dictionary<string, Person>();
        private static ICollection<Person> employees = new ObservableCollection<Person>();

        #region Parsing
        private static readonly Regex AddPersonRegex = new Regex("Add \"(?<Name>\\w+)\", (?<Work>\\d+) for \"(?<Team>\\w+)\"", RegexOptions.Compiled);
        private static readonly Regex UpdateRegex = new Regex("Update \"(?<Name>\\w+)\" (?<Work>\\d+)", RegexOptions.Compiled);
        private static readonly Regex MoveRegex = new Regex("Move \"(?<Name>\\w+)\" to \"(?<Team>\\w+)\"", RegexOptions.Compiled);
        private static readonly Regex RemoveRegex = new Regex("Remove \"(?<Name>\\w+)\"", RegexOptions.Compiled);
        #endregion

        static void Main(string[] args)
        {
            var people = employees.WithUpdates();
            var overpaid = from person in people
                           group person by person.Company into company
                           let min = company.Min(collegue => collegue.Salary)
                           from employee in company
                           where employee.Salary > 12 * min
                           orderby employee.Salary / min
                           select employee;

            overpaid.CollectionChanged += ViolationsChanged;

            var line = Console.ReadLine();
            while (line.ToLowerInvariant() != "exit")
            {
                Match match;
                if (TryMatch(AddPersonRegex, line, out match))
                {
                    var name = match.Groups["Name"].Value;
                    var work = int.Parse(match.Groups["Work"].Value);
                    var team = match.Groups["Team"].Value;

                    try
                    {
                        var person = new Person()
                        {
                            Name = name,
                            Salary = work,
                            Company = team
                        };
                        peopleDict.Add(name, person);
                        employees.Add(person);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
                else if (TryMatch(UpdateRegex, line, out match))
                {
                    var name = match.Groups["Name"].Value;
                    var work = int.Parse(match.Groups["Work"].Value);

                    Person person;
                    if (peopleDict.TryGetValue(name, out person))
                    {
                        Console.WriteLine("Setting {0} to {1} work items.", name, work);
                        person.Salary = work;
                    }
                    else
                    {
                        Console.WriteLine("{0} is not a valid name.", name);
                    }
                }
                else if (TryMatch(MoveRegex, line, out match))
                {
                    var name = match.Groups["Name"].Value;
                    var team = match.Groups["Team"].Value;

                    Person person;
                    if (peopleDict.TryGetValue(name, out person))
                    {
                        Console.WriteLine("Moving {0} to team {1}.", name, team);
                        person.Company = team;
                    }
                    else
                    {
                        Console.WriteLine("{0} is not a valid name.", name);
                    }
                }
                else if (TryMatch(RemoveRegex, line, out match))
                {
                    var name = match.Groups["Name"].Value;

                    Person person;
                    if (peopleDict.TryGetValue(name, out person))
                    {
                        Console.WriteLine("Sacking {0}.", name);
                        employees.Remove(person);
                        peopleDict.Remove(name);
                    }
                    else
                    {
                        Console.WriteLine("{0} is not a valid name.", name);
                    }
                }
                else
                {
                    Console.WriteLine("Unrecognized command.");
                }
                line = Console.ReadLine();
            }
        }

        private static bool TryMatch(Regex regex, string input, out Match match)
        {
            match = regex.Match(input);
            return match.Success;
        }

        private static void ViolationsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (var person in e.OldItems)
                {
                    Console.WriteLine("{0} now has a normal workload.", person);
                }
            }
            if (e.NewItems != null)
            {
                foreach (var person in e.NewItems)
                {
                    Console.WriteLine("Too many work items are assigned to {0}!", person);
                }
            }
        }
    }

    class Person : INotifyPropertyChanged
    {
        #region Name

        private string mName;

        public string Name
        {
            get
            {
                return mName;
            }
            set
            {
                if (mName != value)
                {
                    mName = value;
                    OnNameChanged(EventArgs.Empty);
                }
            }
        }

        protected virtual void OnNameChanged(EventArgs e)
        {
            if (NameChanged != null) NameChanged(this, e);
            OnPropertyChanged("Name");
        }

        public event EventHandler NameChanged;

        #endregion

        #region Salary

        private int mSalary;

        public int Salary
        {
            get
            {
                return mSalary;
            }
            set
            {
                if (mSalary != value)
                {
                    mSalary = value;
                    OnSalaryChanged(EventArgs.Empty);
                }
            }
        }

        protected virtual void OnSalaryChanged(EventArgs e)
        {
            if (SalaryChanged != null) SalaryChanged(this, e);
            OnPropertyChanged("Salary");
        }

        public event EventHandler SalaryChanged;

        #endregion

        #region Company

        private string mCompany;

        public string Company
        {
            get
            {
                return mCompany;
            }
            set
            {
                if (mCompany != value)
                {
                    mCompany = value;
                    OnCompanyChanged(EventArgs.Empty);
                }
            }
        }

        protected virtual void OnCompanyChanged(EventArgs e)
        {
            if (CompanyChanged != null) CompanyChanged(this, e);
            OnPropertyChanged("Company");
        }

        public event EventHandler CompanyChanged;

        #endregion
        
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;

    }
}
